<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者，助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件，未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);

namespace app\services\order;

use app\common\FreeTool;
use app\common\OrderTool;
use app\dao\activity\coupon\StoreCouponUserDao;
use app\kefuapi\controller\Order;
use app\model\activity\coupon\StoreCoupon;
use app\model\activity\free\FreeProductRelation;
use app\model\activity\free\FreeRuleProduct;
use app\model\activity\free\StoreFree;
use app\model\activity\free\StoreFreeRule;
use app\model\activity\seckill\StoreSeckill;
use app\model\activity\seckill\StoreSeckillProduct;
use app\model\order\OrderItems;
use app\model\order\StoreCart;
use app\model\order\StoreOrderItem;
use app\model\product\product\StoreProductRelation;
use app\model\product\sku\StoreProductAttrValue;
use app\model\product\sku\StoreProductStockRelation;
use app\services\activity\advance\StoreAdvanceServices;
use app\services\activity\coupon\StoreCouponUserServices;
use app\services\activity\free\FreeRuleProductsService;
use app\services\BaseServices;
use app\dao\order\StoreCartDao;
use app\services\activity\coupon\StoreCouponIssueServices;
use app\services\pc\CartServices;
use app\services\product\shipping\ShippingTemplatesServices;
use app\services\shipping\ShippingTemplatesNoDeliveryServices;
use app\services\system\SystemUserLevelServices;
use app\services\user\member\MemberCardServices;
use app\services\user\UserServices;
use app\jobs\ProductLogJob;
use Carbon\Carbon;
use crmeb\command\Timer;
use crmeb\exceptions\ApiException;
use crmeb\services\CacheService;
use app\services\activity\seckill\StoreSeckillServices;
use app\services\activity\bargain\StoreBargainServices;
use app\services\activity\combination\StoreCombinationServices;
use app\services\product\product\StoreProductServices;
use app\services\product\sku\StoreProductAttrValueServices;
use think\Db;

/**
 *
 * Class StoreCartServices
 * @package app\services\order
 * @method updateCartStatus($cartIds) 修改购物车状态
 * @method getUserCartNum(int $uid, string $type, int $numType) 购物车数量
 * @method deleteCartStatus(array $cartIds) 修改购物车状态
 * @method array productIdByCartNum(array $ids, int $uid)  根据商品id获取购物车数量
 * @method getCartList(array $where, ?int $page = 0, ?int $limit = 0, ?array $with = []) 获取用户购物车
 * @method getSum($where, $field) 求和
 * @method getProductTrend($time, $timeType, $str) 购物车趋势
 */
class StoreCartServices extends BaseServices
{

    /**
     * StoreCartServices constructor.
     * @param StoreCartDao $dao
     */
    public function __construct(StoreCartDao $dao)
    {
        $this->dao = $dao;
    }

    /**
     * 获取某个用户下的购物车数量
     * @param array $unique
     * @param int $productId
     * @param int $uid
     * @return array
     */
    public function getUserCartNums(array $unique, int $productId, int $uid)
    {
        $where['is_pay'] = 0;
        $where['is_del'] = 0;
        $where['is_new'] = 0;
        $where['product_id'] = $productId;
        $where['uid'] = $uid;
        return $this->dao->getUserCartNums($where, $unique);
    }

    /**
     * 获取用户下的购物车列表
     * @param $uid
     * @param string $cartIds
     * @param bool $new
     * @param array $addr
     * @return array
     * @throws \Psr\SimpleCache\InvalidArgumentException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getUserProductCartListV1($uid, $cartIds = '', bool $new, $addr = [], int $shipping_type = 1)
    {
        if ($new) {
            $cartIds = explode(',', $cartIds);
            $cartInfo = [];
            foreach ($cartIds as $key) {
                $info = CacheService::get($key);
                if ($info) {
                    $cartInfo[] = $info;
                }
            }
        } else {
             $cartInfo = $this->dao->getCartList(['uid' => $uid, 'status'=> 1, 'id' => $cartIds], 0, 0, ['productInfo', 'attrInfo','seckillProduct.seckill'])->toArray();
        }
        if (!$cartInfo) {
            throw new ApiException(410233);
        }
        [$cartInfo, $valid, $invalid] = $this->handleCartList($uid, $cartInfo, $addr, $shipping_type);

        $seckillIds = array_unique(array_column($cartInfo, 'seckill_id'));
        $bargainIds = array_unique(array_column($cartInfo, 'bargain_id'));
        $combinationId = array_unique(array_column($cartInfo, 'combination_id'));
        $advanceId = array_unique(array_column($cartInfo, 'advance_id'));
        $deduction = ['seckill_id' => $seckillIds[0] ?? 0, 'bargain_id' => $bargainIds[0] ?? 0, 'combination_id' => $combinationId[0] ?? 0, 'advance_id' => $advanceId[0] ?? 0];
        return ['cartInfo' => $cartInfo, 'valid' => $valid, 'invalid' => $invalid, 'deduction' => $deduction];
    }

    /**
     * 使用雪花算法生成订单ID
     * @return string
     * @throws \Exception
     */
    public function getCartId($prefix)
    {
        $snowflake = new \Godruoyi\Snowflake\Snowflake();
        //32位
        if (PHP_INT_SIZE == 4) {
            $id = abs((int)$snowflake->id());
        } else {
            $id = $snowflake->setStartTimeStamp(strtotime('2020-06-05') * 1000)->id();
        }
        return $prefix . $id;
    }

    /**
     * 验证库存
     * @param int $uid
     * @param int $cartNum
     * @param string $unique
     * @param int $type
     * @param $productId
     * @param int $seckillId
     * @param int $bargainId
     * @param int $combinationId
     * @return array
     * @throws \Psr\SimpleCache\InvalidArgumentException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function checkProductStock(int $uid, int $cartNum, string $unique, int $type = 0, $productId, int $seckillId, int $bargainId, int $combinationId, int $advanceId)
    {
        /** @var StoreProductAttrValueServices $attrValueServices */
        $attrValueServices = app()->make(StoreProductAttrValueServices::class);
        $stockRelation = null;
        switch ($type) {
            case 0://普通
                if ($unique == '') {
                    $unique = $attrValueServices->value(['product_id' => $productId, 'type' => 0], 'unique');
                }
                /** @var StoreProductServices $productServices */
                $productServices = app()->make(StoreProductServices::class);
                $productInfo = $productServices->isValidProduct($productId);
                if (!$productInfo) {
                    throw new ApiException(410295);
                }

                // 检查库存是从经销商表来的。
                $stockRelation = StoreProductStockRelation::where('product_attr_value_unique', $unique)->find();
                if (!$stockRelation || $stockRelation['product_id'] != $productId) {
                    throw new ApiException(410305);
                }
                $nowStock = $stockRelation->total_stock;
                if ($cartNum > $nowStock) {
                    throw new ApiException(410297, ['num' => $cartNum]);
                }


                $attrInfo = $attrValueServices->getOne(['unique' => $unique, 'type' => 0]);

                // 判断起售  -  如果起售不是0， 且当前库存小于限购数量, 则拦截
                if ($productInfo['limit_min_count'] && $nowStock < $productInfo['limit_min_count']) {
                    throw new ApiException(410238);
                }

                // 判断限购
                if ($productInfo['is_limit'] == 1) {
                    // 永久限购
                    if ($productInfo['limit_type'] == 2) {
                        // 查询已经成交的订单
                        $has_deal_order_count = StoreOrderItem::hasWhere('mainOrder', function ($query) {
                            $query->where('is_del', 0)->whereIn('status', [1, 2, 3])->where('pay_status', 1);
                        })->where('StoreOrderItem.uid', $uid)->where('product_attr_value_unique', $unique)->where('StoreOrderItem.is_del', 0)->count();
                        // 如果是已成交订单数量超过了限购， 则拦截添加
                        if ($has_deal_order_count >= $productInfo['limit_num']) {
                            throw new ApiException(31025);
                        }
                    }

                    // 周期限购
                    if ($productInfo['limit_type'] == 1) {
                        // 查询已经成交的订单
                        $has_deal_order_count = StoreOrderItem::hasWhere('mainOrder', function ($query) use ($productInfo) {
                            $query->where('is_del', 0)->whereIn('status', [1, 2, 3])->where('pay_status', 1);
                            // 获取当前时间的 Carbon 实例
                            $now = Carbon::now();

                            // 根据限购周期类型添加条件
                            $limitCycleType = $productInfo['limit_cycle_type'];
                            switch ($limitCycleType) {
                                case 1: // 天
                                    $limitTime = $now->subDays(1); // 24小时前
                                    $query->where('pay_time', '>=', $limitTime->timestamp);
                                    break;
                                case 2: // 周
                                    $limitTime = $now->subWeeks(1); // 7天前
                                    $query->where('pay_time', '>=', $limitTime->timestamp);
                                    break;
                                case 3: // 月
                                    $limitTime = $now->startOfMonth(); // 当月第一天
                                    $query->where('pay_time', '>=', $limitTime->timestamp);
                                    break;
                                case 4: // 年
                                    $limitTime = $now->startOfYear(); // 年初
                                    $query->where('pay_time', '>=', $limitTime->timestamp);
                                    break;
                                case 0: // 不生效
                                default:
                                    // 无需添加限制条件
                                    break;
                            }
                        })->where('StoreOrderItem.uid', $uid)->where('product_attr_value_unique', $unique)->where('StoreOrderItem.is_del', 0)->count();

                        // 如果是已成交订单数量超过了限购， 则拦截添加
                        if ($has_deal_order_count >= $productInfo['limit_num']) {
                            throw new ApiException(31025);
                        }
                    }

                }

                break;
            case 1://秒杀
                /** @var StoreSeckillServices $seckillService */
                $seckillService = app()->make(StoreSeckillServices::class);
                [$attrInfo, $unique, $productInfo] = $seckillService->checkSeckillStock($uid, $seckillId, $cartNum, $unique);
                break;
            case 2://砍价
                /** @var StoreBargainServices $bargainService */
                $bargainService = app()->make(StoreBargainServices::class);
                [$attrInfo, $unique, $productInfo, $bargainUserInfo] = $bargainService->checkBargainStock($uid, $bargainId, $cartNum, $unique);
                break;
            case 3://拼团
                /** @var StoreCombinationServices $combinationService */
                $combinationService = app()->make(StoreCombinationServices::class);
                [$attrInfo, $unique, $productInfo] = $combinationService->checkCombinationStock($uid, $combinationId, $cartNum, $unique);
                break;
            case 6://预售
                /** @var StoreAdvanceServices $advanceService */
                $advanceService = app()->make(StoreAdvanceServices::class);
                [$attrInfo, $unique, $productInfo] = $advanceService->checkAdvanceStock($uid, $advanceId, $cartNum, $unique);
                break;
            default:
                throw new ApiException(410236);
        }
        if ($type && $type != 6) {
            //根商品规格库存
            $product_stock = $attrValueServices->value(['product_id' => $productInfo['product_id'], 'suk' => $attrInfo['suk'], 'type' => 0], 'stock');
            if ($product_stock < $cartNum) {
                throw new ApiException(410297, ['num' => $cartNum]);
            }
        }
        return [$attrInfo, $unique, $bargainUserInfo['bargain_price_min'] ?? 0, $cartNum, $productInfo, $stockRelation];
    }

    /**
     * 添加购物车
     * @param int $uid 用户UID
     * @param int $product_id 商品ID
     * @param int $cart_num 商品数量
     * @param string $product_attr_unique 商品SKU
     * @param string $type 添加购物车类型
     * @param bool $new true = 立即购买，false = 加入购物车
     * @param int $combination_id 拼团商品ID
     * @param int $seckill_id 秒杀商品ID
     * @param int $bargain_id 砍价商品ID
     * @return mixed|string
     * @throws \Psr\SimpleCache\InvalidArgumentException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function setCart(int $uid, int $product_id, int $cart_num = 1, string $product_attr_unique = '', int $type = 0, bool $new = true, int $combination_id = 0, int $seckill_id = 0, int $bargain_id = 0, int $advance_id = 0)
    {
        if ($cart_num < 1) $cart_num = 1;
//        if ($type == 0) {
//            //检查限购
//            $this->checkLimit($uid, $product_id, $cart_num, $new);
//        }
        //检测库存限量
        [$attrInfo, $product_attr_unique, $bargainPriceMin, $cart_num, $productInfo, $stockRelation] = $this->checkProductStock($uid, $cart_num, $product_attr_unique, $type, $product_id, $seckill_id, $bargain_id, $combination_id, $advance_id);
        
        if ($new) {
            /** @var StoreOrderCreateServices $storeOrderCreateService */
            $storeOrderCreateService = app()->make(StoreOrderCreateServices::class);
            $key = $storeOrderCreateService->getNewOrderId((string)$uid);
            $info['id'] = $key;
            $info['type'] = $type;
            $info['seckill_id'] = $seckill_id;
            $info['bargain_id'] = $bargain_id;
            $info['combination_id'] = $combination_id;
            $info['advance_id'] = $advance_id;
            $info['product_id'] = $product_id;
            $info['product_attr_unique'] = $product_attr_unique;
            $info['cart_num'] = $cart_num;
            $info['productInfo'] = $productInfo ? $productInfo->toArray() : [];
            $info['productInfo']['attrInfo'] = $attrInfo->toArray();
            $info['attrInfo'] = $attrInfo->toArray();
            $info['sum_price'] = $info['productInfo']['attrInfo']['price'];
            //砍价
            if ($bargain_id) {
                $info['truePrice'] = $bargainPriceMin;
                $info['productInfo']['attrInfo']['price'] = $bargainPriceMin;
            } else {
                $info['truePrice'] = $info['productInfo']['attrInfo']['price'] ?? $info['productInfo']['price'] ?? 0;
            }
            //拼团砍价秒杀不参与会员价
            if ($bargain_id || $combination_id || $seckill_id || $advance_id) {
                $info['truePrice'] = $info['productInfo']['attrInfo']['price'] ?? 0;
                $info['vip_truePrice'] = 0;
            }
            $info['trueStock'] = $info['productInfo']['attrInfo']['stock'];
            $info['costPrice'] = $info['productInfo']['attrInfo']['cost'];
            try {
                CacheService::set($key, $info, 3600);
            } catch (\Throwable $e) {
                throw new ApiException($e->getMessage());
            }
            return $key;
        } else {//加入购物车记录

            ProductLogJob::dispatch(['cart', ['uid' => $uid, 'product_id' => $product_id, 'cart_num' => $cart_num]]);
            $cart = $this->dao->getOne(['type' => $type, 'uid' => $uid, 'product_id' => $product_id, 'product_attr_unique' => $product_attr_unique, 'is_del' => 0, 'is_new' => 0, 'is_pay' => 0, 'status' => 1]);
            if ($cart) {

                    $cart->cart_num = $cart_num + $cart->cart_num;
                    $cart->add_time = time();
                    $cart->save();

                    return $cart->id;

            } else {

                $id = $this->transaction(function () use ($uid, $product_attr_unique, $product_id, $cart_num, $type, $stockRelation, $productInfo, $attrInfo) {
                    // 判断存不存在复购 => 后期修改成需要从订单来判断
                    $has_repurchased = StoreOrderItem::hasWhere('mainOrder', function ($query) {
                        $query->where('is_del', 0)->where('status', 3);
                    })->where('StoreOrderItem.uid', $uid)->where('StoreOrderItem.is_del', 0)->where('product_attr_value_unique', $product_attr_unique)->count() ? 1 : 0;
                    // 获取 suk信息
                    $attr_value = StoreProductAttrValue::where('product_id', $product_id)->where('unique', $product_attr_unique)->find();
                    $suk = $attr_value ? $attr_value->suk : '';

                    $is_deal_stock = 0;
                    if ($productInfo['store_type'] == 1) {
                        $is_deal_stock = 1;
                    }


                    $seckill_product_relation = StoreSeckillProduct::hasWhere('seckill', function ($query) {
                        $query->where('start_time', '<=', time())->where('end_time', '>', time())->where('is_del', 0);
                    })->where('product_attr_value_unique', $product_attr_unique)->where('count', '>', 0)->order('sort', 'desc')->find();

                    $seckill_product_relation_id = ($seckill_product_relation) ? $seckill_product_relation->id : 0;
                    $seckill_id = ($seckill_product_relation) ? $seckill_product_relation->id : 0;


                    // 获取秒杀价格
                    $cal_price = ($seckill_product_relation) ? $seckill_product_relation->activity_price : $attr_value->price;
                    $price = $cal_price * $cart_num;

//                    // 补充 满赠信息
//                    $freeInfo = FreeTool::init()->getAvailableFree($product_attr_unique, $price, $cart_num, (bool)$seckill_id, 0);
                    $free_json = json_encode([]);
                    $free_rule_id = 0;
                    $free_rule_uniques = '';
//                    foreach ($freeInfo as $item) {
//                        $free_rule_id = $item['free_rule_id'];
//                        $resultString = '';
//                        for ($i = 0; $i < $item['give_count']; $i++) {
//                            // 将重复的字符串追加到结果字符串
//                            $resultString .= $item['product_attr_value_unique'];
//                            // 如果不是最后一个重复项，添加逗号分隔
//                            if ($i < $item['give_count'] - 1) {
//                                $resultString .= ',';
//                            }
//                        }
//                        $free_rule_uniques .= $resultString;
//                    }
//                    $free_rule_uniques = trim($free_rule_uniques);

                    $add_time = time();
                    $cart_id = $this->dao->save(compact('uid', 'product_id', 'cart_num', 'product_attr_unique',
                        'type', 'add_time', 'has_repurchased', 'suk', 'is_deal_stock', 'seckill_product_relation_id', 'seckill_id',
                        'free_rule_id', 'free_rule_uniques', 'free_json'))->id;
                    return $cart_id;
                });

                return $id;
            }
        }
    }


    public function changeInfo($id, $nums, $sku_unique)
    {
        if (!$id) return new ApiException(410233);
        $cart = StoreCart::with(['productInfo', 'attrInfo', 'seckillProduct.seckill'])->where('id', $id)->where('is_del', 0)->find();
        if (!$cart || $cart->is_pay || !$cart->attrInfo || !$cart->productInfo) return new ApiException(410233);

        $suk = '';
        if ($nums && $sku_unique) {
            // 检查库存
            // 1. 检查是不是符合库存
            $stockRelation = StoreProductStockRelation::where('product_attr_value_unique', $sku_unique)->find();
            if (!$stockRelation) throw new ApiException(410237);
            if ($stockRelation->total_stock < $nums) throw new ApiException(410238);
        }

        if ($cart->productInfo->is_limt == 1) {
            $has_pay_count = 0;
            if ($cart->productInfo->limit_type == 2) {
                // 1. 单词限购, 获取周期维度, 然后搜索订单记录
                $has_pay_count = StoreCart::where('uid', $cart->uid)->where('product_id', $cart->product_id)->where('is_pay', 1)->where('is_del', 0)->count();
            } else if ($cart->productInfo->limit_type == 1) {
                $query = StoreCart::where('uid', $cart->uid)->where('product_id', $cart->product_id)->where('is_pay', 1)->where('is_del', 0);
                switch ($cart->productInfo->limit_cycle_type) {
                    case 1:
                        $todayStart = strtotime(date('Y-m-d 00:00:00'));
                        $todayEnd = strtotime(date('Y-m-d 23:59:59'));

                        $query->where('add_time', '>=', $todayStart)
                            ->where('add_time', '<=', $todayEnd);
                        break;
                    case 2:
                        $weekStart = strtotime(date('Y-m-d 00:00:00', strtotime('last monday')));
                        $weekEnd = strtotime(date('Y-m-d 23:59:59', strtotime('next sunday')));

                        $query->where('add_time', '>=', $weekStart)
                            ->where('add_time', '<=', $weekEnd);
                        break;
                    case 3:
                        $monthStart = strtotime(date('Y-m-01 00:00:00'));
                        $monthEnd = strtotime(date('Y-m-t 23:59:59'));

                        $query->where('add_time', '>=', $monthStart)
                            ->where('add_time', '<=', $monthEnd);
                        break;
                    case 4:
                        $yearStart = strtotime(date('Y-01-01 00:00:00'));
                        $yearEnd = strtotime(date('Y-12-31 23:59:59'));

                        $query->where('add_time', '>=', $yearStart)
                            ->where('add_time', '<=', $yearEnd);
                        break;
                    default:
                        break;
                }
                $has_pay_count = $query->count();
            }
            if ($has_pay_count + $nums > $cart->productInfo->limit_num) {
                throw new ApiException(410240, ['limit' => $cart->productInfo->limit_num, 'pay_num'   => $has_pay_count]);
            }

        }

        if ($nums && $cart->cart_num == $nums) return true;
        if ($nums) {
            $this->dao->changeUserCartNum(['uid' => $cart->uid, 'id' => $id], (int)$nums);
        }
        if ($sku_unique) {
            $this->dao->update($id, ['product_attr_unique' => $sku_unique, 'suk' => $suk]);
        }

        return true;

    }


    public function gerPriceInfo($ids, $coupon_id, $uid)
    {
        $ids = array_filter($ids);
        if (empty($ids)) throw new ApiException(31005);
        $carts = StoreCart::with(['productInfo', 'attrInfo', 'seckillProduct.seckill'])->whereIn('id', $ids)->where('uid', $uid)->where('is_del', 0)->where('is_pay', 0)->select();
        if ($carts->count() != count($ids)) throw new ApiException(31004);

        $price = $coupon_price = 0;

        foreach ($carts as $cart) {

            $activity_price = $cart->seckillProduct ? $cart->seckillProduct->activity_price : 0;

            $price += ($activity_price) ? $activity_price * $cart->cart_num: $cart->attrInfo->price * $cart->cart_num;
        }

        $couponServers = app()->make(StoreCouponIssueServices::class);
        $list = $couponServers->beUsableCouponList($uid,$ids,false);
        $list = array_column($list,null,'cid');
        if ($coupon_id) {
            if(isset($list[$coupon_id])) $coupon_price = $list[$coupon_id]['coupon_price'];
        }else{//自动计算可使用优惠券
            foreach($list as $val){
                if($coupon_price == 0) {
                    $coupon_price = $val['coupon_price'];
                    $coupon_id = $val['cid'];
                }
                if($val['coupon_price'] > $coupon_price){
                    $coupon_price = $val['coupon_price'];
                    $coupon_id = $val['cid'];
                }
            }
        }

        return [
            'fact_price' =>  $price - $coupon_price,
            'coupon_price' => $coupon_price,
            'coupon_id' => $coupon_id
        ];
    }

    /**
     * @param array $ids
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getReducePriceDetail(array $ids,int $coupon_id,int $uid)
    {
        $ids = array_filter($ids);
        if (empty($ids)) return [];
        $carts = StoreCart::with(['productInfo', 'attrInfo', 'seckillProduct.seckill'])->where('uid', $uid)->whereIn('id', $ids)->select();

        $total_price = $seckill_reduce_price = $coupon_price = 0;
        $list = app()->make(StoreCouponIssueServices::class)->beUsableCouponList($uid,$ids,false);
        $list = array_column($list,null,'cid');

        if ($coupon_id) {
            if(isset($list[$coupon_id])) $coupon_price = $list[$coupon_id]['coupon_price'];
        }else{//自动计算可使用优惠券
            foreach($list as $val){
                if($coupon_price == 0) {
                    $coupon_price = $val['coupon_price'];
                    $coupon_id = $val['cid'];
                }
                if($val['coupon_price'] > $coupon_price){
                    $coupon_price = $val['coupon_price'];
                    $coupon_id = $val['cid'];
                }
            }
        }
        $products = [];
        foreach ($carts as $cart) {

            $products[] = ($cart->attrInfo->image) ? $cart->attrInfo->image : '';

            // 获取总价
            $total_price += $cart->attrInfo ? $cart->attrInfo->price * $cart->cart_num : 0;  // 200 或者 100
            // 获取秒杀价格
            // 查找是不是秒杀商品
            $seckill_price = ($cart->seckillProduct) ? $cart->seckillProduct->activity_price : 0;  // 100 或者 0
            $seckill_reduce_price += $seckill_price ? ($cart->attrInfo->price - $seckill_price) * $cart->cart_num : 0;
        }

        return [
            'total_price'   =>  $total_price,
            'seckill_reduce_price' =>  $seckill_reduce_price,
            'coupon_price'  =>  $coupon_price,
            'coupon_id' => $coupon_id,
            'total_reduce_price'    =>  ($seckill_reduce_price + $coupon_price),
            'fact_price'    =>  $total_price - $seckill_reduce_price - $coupon_price,
            'products'   => $products
        ];
    }


    /**移除购物车商品
     * @param int $uid
     * @param array $ids
     * @return StoreCartDao|bool
     */
    public function removeUserCart(int $uid, array $ids)
    {
        if (!$uid || !$ids) return false;
        StoreCart::where('uid', $uid)->whereIn('id', $ids)->update(['is_del' => 1]);
        return true;
//        return $this->dao->removeUserCart($uid, $ids);
    }


    public function clearInvalid(int $uid)
    {
        $carts = StoreCart::with(['productInfo'])->where('uid', $uid)->where('is_del', 0)->where('is_pay', 0)->select();
        if (!$carts) return true;

        $ids = [];
        foreach ($carts as $cart) {

            if (!$cart->productInfo || $cart->productInfo->is_del == 1 || $cart->is_show == 0) {
                $ids[] = $cart->id;
            }
        }

        $this->dao->batchUpdate($ids, ['is_del' => 1]);
        return true;
    }



    /**购物车 修改商品数量
     * @param $id
     * @param $number
     * @param $uid
     * @return bool|\crmeb\basic\BaseModel
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function changeUserCartNum($id, $number, $uid)
    {
        if (!$id || !$number || !$uid) return false;
        $where = ['uid' => $uid, 'id' => $id];
        $carInfo = $this->dao->getOne($where, 'product_id,combination_id,seckill_id,bargain_id,product_attr_unique,cart_num');

        //购物车修改数量检查限购
        /** @var StoreProductServices $productServices */
        $productServices = app()->make(StoreProductServices::class);
        $limitInfo = $productServices->get($carInfo->product_id, ['is_limit', 'limit_type', 'limit_num']);

        if ($limitInfo['is_limit']) {
            if ($limitInfo['limit_type'] == 1 && $number > $limitInfo['limit_num']) {
                throw new ApiException(410239, ['limit' => $limitInfo['limit_num']]);
            } else if ($limitInfo['limit_type'] == 2) {
                /** @var StoreOrderCartInfoServices $orderCartServices */
                $orderCartServices = app()->make(StoreOrderCartInfoServices::class);
                $orderPayNum = $orderCartServices->sum(['uid' => $uid, 'product_id' => $carInfo->product_id], 'cart_num');
                $orderRefundNum = $orderCartServices->sum(['uid' => $uid, 'product_id' => $carInfo->product_id], 'refund_num');
                $orderNum = $orderPayNum - $orderRefundNum;
                if (($number + $orderNum) > $limitInfo['limit_num']) {
                    throw new ApiException(410240, ['limit' => $limitInfo['limit_num'], 'pay_num' => $orderNum]);
                }
            }
        }

        $stock = $productServices->getProductStock($carInfo->product_id, $carInfo->product_attr_unique);
        if (!$stock) throw new ApiException(410237);
        if ($stock < $number) throw new ApiException(410297, ['num' => $number]);
        if ($carInfo->cart_num == $number) return true;
        return $this->dao->changeUserCartNum(['uid' => $uid, 'id' => $id], (int)$number);
    }

    /**
     * 修改购物车状态
     * @param int $productId
     * @param int $status 0 商品下架
     */
    public function changeStatus(int $productId, $status = 0)
    {
        $this->dao->update($productId, ['status' => $status], 'product_id');
    }

    /**
     * 获取购物车列表
     * @param int $uid
     * @param int $status
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getUserCartList(int $uid, int $status, string $cartIds = '', int $type = 1)
    {
        [$page, $limit] = $this->getPageValue();
        $list = $this->dao->getCartList(['uid' => $uid, 'status' => $status, 'id' => $cartIds, 'type' => $type], $page, $limit, ['productInfo', 'attrInfo', 'seckillProduct.seckill', 'freeRule']);

        // 处理展示
        $valid = $invalid = [];
        $fact_price = $total_price = $reduce_price = $seckill_price = $coupon_price = 0;

        foreach ($list as $item) {
            // 判断有没有失效的依据 对应 unique 能不能在 zg_store_product_attr_value 找的到了
            if ($item->productInfo && $item->productInfo->is_show == 0) {
                $invalid[] = [
                    'id' => $item->id,
                    'image' => $item->attrInfo ? $item->attrInfo->image : '',
                    'stock_name' => $item->productInfo->store_name,
                    'attr_value' => $item->attrInfo ? $item->attrInfo->suk : '',
                    'reason' => '已下架'
                ];
            } else {
                $cart_nums = $item->cart_num;

                $price = $item->attrInfo ? $item->attrInfo->price : 0;
                $seckill_price = ($item->seckillProduct) ? $item->seckillProduct->activity_price : 0;

                $stock = ($item->attrInfo && $item->attrInfo->stockRelation) ? $item->attrInfo->stockRelation->total_stock : 0;

                // 处理满赠信息
                $fact_alone_price = ($seckill_price) ? $seckill_price * $cart_nums : $price * $cart_nums;
                $free_info = FreeTool::init()->pickDefaultAvailableFree($item->product_attr_unique, $fact_alone_price, $cart_nums, (bool)$seckill_price, false);

                $info = [
                    'id' => $item->id,
                    'image' => $item->attrInfo ? $item->attrInfo->image : '',
                    'store_name' => $item->productInfo ? $item->productInfo->store_name : '',
                    'attr_value' => $item->attrInfo ? $item->attrInfo->suk : '',
                    'has_seckill' => $item->seckillProduct ? 1 : 0,
                    'seckill' => $item->seckillProduct && $item->seckillProduct->seckill ? [
                        'relation_id' => $item->seckillProduct->id ?? 0,
                        'start_time' => $item->seckillProduct->seckill->start_time ?? 0,
                        'end_time' => $item->seckillProduct->seckill->end_time ?? 0,
                    ] : [],
                    'product_id'    =>  $item->product_id,
//                    'free_info' => OrderTool::init()->getFreeProductInfo($item->free_json) ?: new EmptyObject(),
                    'free_info' =>  $free_info,
                    'sku_unique' => ($item->attrInfo) ? $item->attrInfo->unique : '',
                    'cart_num' => $cart_nums,
                    'price' => $price,
                    "activity_price" => $seckill_price,
                    'stock' => $stock
                ];

                $total_price += $info['price'] * $cart_nums;
                $fact_price += $fact_alone_price;

                $valid[] = $info;
            }

        }

        return [
            'valid' => $valid,
            'invalid' => $invalid,
            'total_price' => $total_price,
            'fact_price' => $fact_price,
            'reduce_price' => $reduce_price,
            'shipping_price' => 0, // 物流金额
            'seckill_price' => 0, // 秒杀立减
            'coupon_price' => $coupon_price, // 优惠券金额
        ];
    }

    public function getFreeInfo($frees, $cal_price, $cart_nums)
    {
        $free_info = [];
        foreach ($frees as $free) {

            // 获取所有规则
            if ($free->is_del == 1 || !($free->start_time < time() && $free->end_time >= time()) || $free->status == 0 || !$free->rules) continue;

            foreach ($free->rules as $k => $rule) {
                $free_info[$k]['id'] = $rule->id;
                $free_info[$k]['nums'] = $rule->nums;

                if ($free->type == 1 && $rule->type == 1 && $rule->min_price <= $cal_price) { // 满额

                    // 获取第一个商品
                    $list = [];
                    $i = 0;
                    foreach ($rule->products as $products) {
                        // 赠送件数  -> 赠送库存
                        if ($products->activity_count < $products->give_count) continue;

                        if ($i >= $rule->nums) break;

                        $list[] = [
                            'store_name'    =>  $products->product->store_name,
                            'unique'    =>  $products->attrInfo->unique,
                            'suk'   =>  $products->attrInfo->suk,
                            'image' =>  $products->attrInfo->image,
                            'price' =>  $products->attrInfo->price,
                            'show_price'    =>  "0.00",
                            'give_count'  =>  $products->give_count
                        ];
                        $i++;
                    }
                    $free_info[$k]['list'] = $list;
                }
                if ($free->type == 2 && $rule->type == 2 && $rule->number <= $cart_nums) { // 满件

                    // 获取第一个商品
                    $list = [];
                    $i = 0;
                    foreach ($rule->products as $products) {
                        // 赠送件数  -> 赠送库存
                        if ($products->activity_count < $products->give_count) continue;

                        if ($i >= $rule->nums)break;
                        $list[] = [
                            'unique'    =>  $products->product_attr_value_unique,
                            'suk'   =>  $products->product->suk,
                            'image' =>  $products->product->image,
                            'price' =>  $products->product->price,
                            'give_count'  =>  $products->give_count
                        ];
                    }
                    $free_info[$k]['list'] = $list;
                }

            }
        }

        return $free_info;
    }

    /**
     * 购物车重选
     * @param int $cart_id
     * @param int $product_id
     * @param string $unique
     */
    public function modifyCart(int $cart_id, int $product_id, string $unique)
    {
        /** @var StoreProductAttrValueServices $attrService */
        $attrService = app()->make(StoreProductAttrValueServices::class);
        $stock = $attrService->value(['product_id' => $product_id, 'unique' => $unique, 'type' => 0], 'stock');
        if ($stock > 0) {
            $this->dao->update($cart_id, ['product_attr_unique' => $unique, 'cart_num' => 1]);
        } else {
            throw new ApiException(410238);
        }
    }

    /**
     * 重选购物车
     * @param $id
     * @param $uid
     * @param $productId
     * @param $unique
     * @param $num
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function resetCart($id, $uid, $productId, $unique, $num)
    {
        $res = $this->dao->getOne(['uid' => $uid, 'product_id' => $productId, 'product_attr_unique' => $unique]);
        if ($res) {
            $res->cart_num = $res->cart_num + $num;
            $res->save();
            $this->dao->delete($id);
        } else {
            $this->dao->update($id, ['product_attr_unique' => $unique, 'cart_num' => $num]);
        }
    }

    /**
     * 首页加入购物车
     * @param $uid
     * @param $productId
     * @param $num
     * @param $unique
     * @param $type
     * @return mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function setCartNum($uid, $productId, $num, $unique, $type)
    {
        if ($type == 1) {
            //检查限购
            $this->checkLimit($uid, $productId, $num, 0);
        }

        /** @var StoreProductAttrValueServices $attrValueServices */
        $attrValueServices = app()->make(StoreProductAttrValueServices::class);

        if ($unique == '') {
            $unique = $attrValueServices->value(['product_id' => $productId, 'type' => 0], 'unique');
        }
        /** @var StoreProductServices $productServices */
        $productServices = app()->make(StoreProductServices::class);

        if (!$productServices->isValidProduct((int)$productId, 'id')) {
            throw new ApiException(410295);
        }
        if (!($unique && $attrValueServices->getAttrvalueCount($productId, $unique, 0))) {
            throw new ApiException(410305);
        }
        if ($productServices->getProductStock((int)$productId, $unique) < $num) {
            throw new ApiException(410297, ['num' => $num]);
        }

        $cart = $this->dao->getOne(['uid' => $uid, 'product_id' => $productId, 'product_attr_unique' => $unique]);
        if ($cart) {
            if ($type == -1) {
                $cart->cart_num = $num;
            } elseif ($type == 0) {
                $cart->cart_num = $cart->cart_num - $num;
            } elseif ($type == 1) {
                $cart->cart_num = $cart->cart_num + $num;
            }
            if ($cart->cart_num === 0) {
                return $this->dao->delete($cart->id);
            } else {
                $cart->add_time = time();
                $cart->save();
                return $cart->id;
            }
        } else {
            $data = [
                'uid' => $uid,
                'product_id' => $productId,
                'cart_num' => $num,
                'product_attr_unique' => $unique,
                'type' => 0,
                'add_time' => time()
            ];
            return $this->dao->save($data)->id;
        }
    }

    /**
     * 获取用户购物车数量  ids 统计金额
     * @param int $uid
     * @param string $numType
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getUserCartCount(int $uid, string $numType)
    {
        $count = 0;
        $ids = [];
        $sum_price = 0;
        $cartList = $this->dao->getUserCartList($uid, '*', ['productInfo', 'attrInfo']);
        if ($cartList) {
            /** @var StoreProductServices $productServices */
            $productServices = app()->make(StoreProductServices::class);
            /** @var MemberCardServices $memberCardService */
            $memberCardService = app()->make(MemberCardServices::class);
            $vipStatus = $memberCardService->isOpenMemberCard('vip_price', false);
            /** @var UserServices $user */
            $user = app()->make(UserServices::class);
            $userInfo = $user->getUserInfo($uid);
            $discount = 100;
            if (sys_config('member_func_status', 1)) {
                /** @var SystemUserLevelServices $systemLevel */
                $systemLevel = app()->make(SystemUserLevelServices::class);
                $discount = $systemLevel->value(['id' => $userInfo['level'], 'is_del' => 0, 'is_show' => 1], 'discount') ?: 100;
            }
            foreach ($cartList as &$item) {
                $productInfo = $item['productInfo'];
                if (isset($productInfo['attrInfo']['product_id']) && $item['product_attr_unique']) {
                    [$truePrice, $vip_truePrice, $type] = $productServices->setLevelPrice($productInfo['attrInfo']['price'] ?? 0, $uid, $userInfo, $vipStatus, $discount, $productInfo['attrInfo']['vip_price'] ?? 0, $productInfo['is_vip'] ?? 0, true);
                    $item['truePrice'] = $truePrice;
                    $item['price_type'] = $type;
                } else {
                    [$truePrice, $vip_truePrice, $type] = $productServices->setLevelPrice($item['productInfo']['price'] ?? 0, $uid, $userInfo, $vipStatus, $discount, $item['productInfo']['vip_price'] ?? 0, $item['productInfo']['is_vip'] ?? 0, true);
                    $item['truePrice'] = $truePrice;
                    $item['price_type'] = $type;
                }
                $sum_price = bcadd((string)$sum_price, (string)bcmul((string)$item['cart_num'], (string)$item['truePrice'], 4), 2);
            }
            $ids = array_column($cartList, 'id');
            if ($numType) {
                $count = count($cartList);
            } else {
                $count = array_sum(array_column($cartList, 'cart_num'));
            }
        }
        return compact('count', 'ids', 'sum_price');
    }

    /**
     * 处理购物车数据
     * @param int $uid
     * @param array $cartList
     * @param array $addr
     * @param int $shipping_type
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * @author 吴汐
     * @email 442384644@qq.com
     * @date 2023/02/16
     */
    public function handleCartList(int $uid, array $cartList, array $addr = [], int $shipping_type = 1, int $type = 1)
    {
        if (!$cartList) return [$cartList, [], []];
        $tempIds = [];
        $userInfo = [];
        $discount = 100;
        if ($uid) {
            /** @var UserServices $user */
            $user = app()->make(UserServices::class);
            $userInfo = $user->getUserInfo($uid);
            //用户等级是否开启
            if (sys_config('member_func_status', 1)) {
                /** @var SystemUserLevelServices $systemLevel */
                $systemLevel = app()->make(SystemUserLevelServices::class);
                $discount = $systemLevel->value(['id' => $userInfo['level'], 'is_del' => 0, 'is_show' => 1], 'discount') ?: 100;
            }
        }

        //付费会员是否开启，用户是否是付费会员，两个都满足，订单计算金额才会按照付费会员计算。
        /** @var MemberCardServices $memberCardService */
        $memberCardService = app()->make(MemberCardServices::class);
        $vipStatus = $memberCardService->isOpenMemberCard('vip_price', false) && $userInfo['is_money_level'] > 0;

        //不送达运费模板
        if ($shipping_type == 1 && $addr) {
            $cityId = (int)($addr['city_id'] ?? 0);
            if ($cityId) {
                foreach ($cartList as $item) {
                    $tempIds[] = $item['productInfo']['temp_id'];
                }
                $tempIds = array_unique($tempIds);
                $shippingService = app()->make(\app\services\shipping\ShippingTemplatesServices::class);
                $tempIds = $shippingService->getColumn([['id', 'in', $tempIds], ['no_delivery', '=', 1]], 'id');
                if ($tempIds) {
                    /** @var ShippingTemplatesNoDeliveryServices $noDeliveryServices */
                    $noDeliveryServices = app()->make(ShippingTemplatesNoDeliveryServices::class);
                    $tempIds = $noDeliveryServices->isNoDelivery(array_unique($tempIds), $cityId);
                }
            }
        }

        /** @var StoreProductServices $productServices */
        $productServices = app()->make(StoreProductServices::class);
        $valid = $invalid = [];
        foreach ($cartList as &$item) {
            $item['productInfo']['express_delivery'] = false;
            $item['productInfo']['store_mention'] = false;
            if (isset($item['productInfo']['logistics'])) {
                if (in_array(1, explode(',', $item['productInfo']['logistics']))) {
                    $item['productInfo']['express_delivery'] = true;
                }
                if (in_array(2, explode(',', $item['productInfo']['logistics']))) {
                    $item['productInfo']['store_mention'] = true;
                }
            }
            if (isset($item['attrInfo']) && $item['attrInfo'] && (!isset($item['productInfo']['attrInfo']) || !$item['productInfo']['attrInfo'])) {
                $item['productInfo']['attrInfo'] = $item['attrInfo'] ?? [];
            }
            $item['attrStatus'] = isset($item['productInfo']['attrInfo']['stock']) && $item['productInfo']['attrInfo']['stock'];
            $item['productInfo']['attrInfo']['image'] = $item['productInfo']['attrInfo']['image'] ?? $item['productInfo']['image'] ?? '';
            $item['productInfo']['attrInfo']['suk'] = $item['productInfo']['attrInfo']['suk'] ?? '已失效';
            if (isset($item['productInfo']['attrInfo'])) {
                $item['productInfo']['attrInfo'] = get_thumb_water($item['productInfo']['attrInfo']);
            }
            $item['productInfo'] = get_thumb_water($item['productInfo']);
            $productInfo = $item['productInfo'];
            $item['vip_truePrice'] = 0;
            $is_activity = $item['seckill_id'] || $item['bargain_id'] || $item['combination_id'] || $item['advance_id'];
            if (isset($productInfo['attrInfo']['product_id']) && $item['product_attr_unique']) {
                $item['costPrice'] = $productInfo['attrInfo']['cost'] ?? 0;
                $item['trueStock'] = $productInfo['attrInfo']['stock'] ?? 0;
                $item['truePrice'] = $productInfo['attrInfo']['price'] ?? 0;
                $item['sum_price'] = $productInfo['attrInfo']['price'] ?? 0;
                if (!$is_activity) {
                    [$truePrice, $vip_truePrice, $type] = $productServices->setLevelPrice($productInfo['attrInfo']['price'] ?? 0, $uid, $userInfo, $vipStatus, $discount, $productInfo['attrInfo']['vip_price'] ?? 0, $productInfo['is_vip'] ?? 0, true);
                    $item['truePrice'] = $truePrice;
                    $item['vip_truePrice'] = $vip_truePrice;
                    $item['price_type'] = $type;
                } else {
                    $item['price_type'] = 'activity';
                }
            } else {
                $item['costPrice'] = $item['productInfo']['cost'] ?? 0;
                $item['trueStock'] = $item['productInfo']['stock'] ?? 0;
                $item['truePrice'] = $item['productInfo']['price'] ?? 0;
                $item['sum_price'] = $item['productInfo']['price'] ?? 0;
                if (!$is_activity) {
                    [$truePrice, $vip_truePrice, $type] = $productServices->setLevelPrice($item['productInfo']['price'] ?? 0, $uid, $userInfo, $vipStatus, $discount, $item['productInfo']['vip_price'] ?? 0, $item['productInfo']['is_vip'] ?? 0, true);
                    $item['truePrice'] = $truePrice;
                    $item['vip_truePrice'] = $vip_truePrice;
                    $item['price_type'] = $type;
                } else {
                    $item['price_type'] = 'activity';
                }
            }
            if (isset($item['status']) && $item['status'] == 0) {
                $item['is_valid'] = 0;
                $invalid[] = $item;
            }elseif(!empty($item['seckillProduct'])){
                $item['is_valid'] = 0;
                $invalid[] = $item;
            } else {
                switch ($shipping_type) {
                    case 1:
                        //不送达
                        if (in_array($item['productInfo']['temp_id'], $tempIds) || (isset($item['productInfo']['logistics']) && !in_array(1, explode(',', $item['productInfo']['logistics'])) && $item['productInfo']['logistics'] != 0)) {
                            $item['is_valid'] = 0;
                            $invalid[] = $item;
                        } else {
                            $item['is_valid'] = 1;
                            $valid[] = $item;
                        }
                        break;
                    case 2:
                        //不支持到店自提
                        if (isset($item['productInfo']['logistics']) && $item['productInfo']['logistics'] && !in_array(2, explode(',', $item['productInfo']['logistics'])) && $item['productInfo']['logistics'] != 0) {
                            $item['is_valid'] = 0;
                            $invalid[] = $item;
                        } else {
                            $item['is_valid'] = 1;
                            $valid[] = $item;
                        }
                        break;
                }
            }
            unset($item['attrInfo']);
        }
        return [$cartList, $valid, $invalid];
    }

    /**
     * 检查限购
     * @param $uid
     * @param $product_id
     * @param $num
     * @param $new
     * @return bool
     * @throws \ReflectionException
     */
    public function checkLimit($uid, $product_id, $num, $new)
    {
        /** @var StoreProductServices $productServices */
        $productServices = app()->make(StoreProductServices::class);
        /** @var StoreOrderCartInfoServices $orderCartServices */
        $orderCartServices = app()->make(StoreOrderCartInfoServices::class);

        $limitInfo = $productServices->get($product_id, ['is_limit', 'limit_type', 'limit_num']);
        if (!$limitInfo) throw new ApiException(410294);
        $limitInfo = $limitInfo->toArray();
        if (!$limitInfo['is_limit']) return true;
        if ($limitInfo['limit_type'] == 1) {
            $cartNum = 0;
            if (!$new) {
                $cartNum = $this->dao->sum(['uid' => $uid, 'product_id' => $product_id], 'cart_num');
            }
            if (($num + $cartNum) > $limitInfo['limit_num']) {
                throw new ApiException(410239, ['limit' => $limitInfo['limit_num']]);
            }
        } else if ($limitInfo['limit_type'] == 2) {
            $cartNum = $this->dao->sum(['uid' => $uid, 'product_id' => $product_id], 'cart_num');
            $orderPayNum = $orderCartServices->sum(['uid' => $uid, 'product_id' => $product_id, 'split_status' => 0], 'cart_num');
            $orderRefundNum = $orderCartServices->sum(['uid' => $uid, 'product_id' => $product_id, 'split_status' => 0], 'refund_num');
            $orderNum = $orderPayNum - $orderRefundNum;
            if (($num + $orderNum + $cartNum) > $limitInfo['limit_num']) {
                throw new ApiException(410240, ['limit' => $limitInfo['limit_num'], 'pay_num' => $orderNum]);
            }
        }
        return true;
    }
}

class EmptyObject{}
